//========================================================= 
/**@file TankUI.h
 * @brief 
 * 
 * @date 2025-07-18   12:04:19
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_TANKUI_H_
#define _LIBZHOUYB_TANKUI_H_
//--------------------------------------------------------- 
#include "Tank.h"

#include "../../tools/AsyncWorker.h"
using zhou_yb::application::tools::AsyncWorker;

#include "../../../wrapper/imgui/imgui_extractor.h"
#include "../../../wrapper/imgui/env/imgui_dx11.h"
using zhou_yb::wrapper::imgui_extractor::GuiDX11;
using zhou_yb::wrapper::imgui_extractor::GuiWindow;

#include "../../../extension/ability/win_helper/WinCharsetConvert.h"
using zhou_yb::extension::ability::WinCharsetConvert;

#include "../../../include/Extension.h"

#ifndef LOG
#   define LOG LogCat_Default("")
#endif
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace game {
namespace tank90 {
//--------------------------------------------------------- 
struct TankGuiMeta
{
    std::string name;
    uint color;
    bool visible;

    TankGuiMeta() : name(""), color(0), visible(true) {}
};
//--------------------------------------------------------- 
class TankBlinkReward : public TankReward
{
protected:
    TickTimer _timer;
public:
    TankBlinkReward()
    {
        _timer.SetTimeout(5);
    }
    virtual void OnFrame()
    {
        TankReward::OnFrame();

        if (_timer.Elapsed())
        {
            _timer.Restart();

            TankGuiMeta* m = (TankGuiMeta*)meta;
            m->visible = !m->visible;
        }
    }

    virtual void Serialize(BinaryEncoder& se) const
    {
        TankReward::Serialize(se);

        TankGuiMeta* m = (TankGuiMeta*)meta;
        se << _timer.TickCount() << m->visible;
    }
    virtual void Deserialize(BinaryDecoder& se)
    {
        TankReward::Deserialize(se);

        TankGuiMeta* m = (TankGuiMeta*)meta;
        uint tick = 0;
        se << tick << m->visible;
        _timer.Forward(tick);
    }
};
//--------------------------------------------------------- 
struct ITankRender
{
    virtual void OnDraw(TBody2D* body, TankGuiMeta* meta) = 0;
};
//--------------------------------------------------------- 
class TankGuiRender : public ITankRender, public RefObject
{
public:
    ImVec2 Position;
    uint ZoomSize;
public:
    TankGuiRender() : ZoomSize(1) {}

    virtual void OnDraw(TBody2D* body, TankGuiMeta* meta) override
    {
        ImDrawList* dw = ImGui::GetWindowDrawList();
        ImU32 color = (meta->color | 0xFF000000);
        ImVec2 p1 = ImVec2(body->position.x / ZoomSize, body->position.y / ZoomSize) + Position;
        ImVec2 p2 = p1;

        uint w = body->size.x / ZoomSize;
        uint h = body->size.y / ZoomSize;
        p2.x += w;

        p2.y += h;

        TNode2D* node = (TNode2D*)body;
        if (node->class_type == class_Enemy || node->class_type == class_Player)
        {
            Tank* tank = (Tank*)body;
            uint barrel_size = 40 / ZoomSize;
            ImVec2 p;
            if (node->direction == TVector2_UP)
            {
                p = p1;
                p.x += (w - barrel_size) / 2;
                dw->AddRectFilled(p, ImVec2(p.x + barrel_size, p.y + barrel_size), color);

                p1.y += barrel_size;
            }
            else if (node->direction == TVector2_DOWN)
            {
                p = p1;
                p.x += (w - barrel_size) / 2;
                p.y += h - barrel_size;
                dw->AddRectFilled(p, ImVec2(p.x + barrel_size, p.y + barrel_size), color);

                p2.y -= barrel_size;
            }
            else if (node->direction == TVector2_LEFT)
            {
                p = p1;
                p.y += (h - barrel_size) / 2;

                p1.x += barrel_size;
            }
            else if (node->direction == TVector2_RIGHT)
            {
                p = p1;
                p.x += w - barrel_size;
                p.y += (h - barrel_size) / 2;

                p2.x -= barrel_size;
            }
            if (p.x || p.y)
            {
                dw->AddRectFilled(p, ImVec2(p.x + barrel_size, p.y + barrel_size), color);
            }

            ImGui::PushStyleColor(ImGuiCol_Text, 0xFFFFBF00);
            ImGui::SetCursorPos(ImVec2(p1.x, p1.y - 10));
            //ImGui::Text("HP:%d", tank->Hp);
            ImGui::Text("ID:%d", tank->Uid);
            if (!meta->name.empty())
            {
                ImGui::SetCursorPos(ImVec2(p1.x, p1.y + 10));
                ImGui::Text("%s", meta->name.c_str());
            }
            ImGui::PopStyleColor();
        }

        dw->AddRectFilled(p1, p2, color);
    }
};
//--------------------------------------------------------- 
class TankEngineObserver : public ITEngineObserver, public RefObject
{
protected:
    std::vector<Ref<ITankRender> > _renders;
public:
    void AddRender(Ref<ITankRender> render)
    {
        _renders.push_back(render);
    }

    void Clear()
    {
        _renders.clear();
    }

    virtual void OnInit(TBody2D* body) override
    {
        TankGuiMeta* meta = new TankGuiMeta();
        TNode2D* node = ctype_cast(TNode2D*)(body);
        switch (node->class_type)
        {
        case class_Player:
            meta->color = RGB(34, 144, 255);
            break;
        case class_Enemy:
            meta->color = RGB(232, 232, 232);
            break;
        case class_Barrier:
            if (node->group == barrier_Steel)
            {
                meta->color = RGB(255, 255, 255);
            }
            else if (node->group == barrier_Brick)
            {
                meta->color = RGB(139, 105, 20);
            }
            else if (node->group == barrier_Water)
            {
                meta->color = RGB(30, 144, 255);
            }
            else
            {
                meta->color = RGB(220, 220, 220);
            }
            break;
        case class_Reward:
            meta->color = RGB(255, 215, 0);
            break;
        case class_Bullet:
            meta->color = RGB(255, 250, 250);
            break;
        }
        body->meta = meta;
    }

    virtual void OnFini(TBody2D* body) override
    {
        if (body->meta)
        {
            delete body->meta;
            body->meta = NULL;
        }
    }

    virtual void OnFrame(TBody2D* body) override
    {
        ((TNode2D*)body)->OnFrame();
    }

    virtual void OnUpdate(TBody2D* body) override
    {
        
    }

    virtual void OnCollision(TBody2D* body, list_t<TCollisionData2D>& data) override
    {
        ((TNode2D*)body)->OnCollision(data);
    }

    virtual void OnFixedFrame(list_t<TBody2D*>& bodies) override
    {
        for (list_t<TBody2D*>::iterator itr = bodies.begin(); itr != bodies.end(); ++itr)
        {
            TankGuiMeta* meta = (TankGuiMeta*)(*itr)->meta;
            if (meta && meta->visible)
            {
                for (int i = 0;i < _renders.size(); ++i)
                {
                    _renders[i]->OnDraw(*itr, meta);
                }
            }
        }
    }
};
//--------------------------------------------------------- 
class TankFactory : public ITankFactory
{
public:
    virtual TNode2D* Create(uint class_type, uint group_type) override
    {
        switch (class_type)
        {
        case class_Player:
            return new TankPlayer();
        case class_Barrier:
            return TankBarrier::Create(group_type);
        case class_Enemy:
            return new TankEnemy();
        case class_Reward:
            return new TankBlinkReward();
        case class_Bullet:
            return new TankBullet();
        }
        return NULL;
    }
    virtual void Destroy(TNode2D* node) override
    {
        delete node;
    }
};
//--------------------------------------------------------- 
struct TankTile : public ISerializable
{
    byte class_type;
    byte group_type;
    byte x;
    byte y;

    void Serialize(BinaryEncoder& se) const override
    {
        se << class_type << group_type << x << y;
    }
    void Deserialize(BinaryDecoder& se) override
    {
        se << class_type << group_type << x << y;
    }
};
//--------------------------------------------------------- 
/// 地图信息
struct TankMap : public ISerializable
{
    uint w;
    uint h;

    std::vector<TankTile> tiles;

    TankMap() : w(10), h(10) {}

    void Serialize(BinaryEncoder& se) const override
    {
        uint tile_count = tiles.size();
        se << w << h << tile_count;
        for (uint i = 0; i < tile_count; ++i)
        {
            tiles[i].Serialize(se);
        }
    }
    void Deserialize(BinaryDecoder& se) override
    {
        uint tile_count = 0;
        se << w << h << tile_count;

        TankTile t;
        for (uint i = 0; i < tile_count; ++i)
        {
            t.Deserialize(se);
            tiles.push_back(t);
        }
    }
};
//--------------------------------------------------------- 
struct TankUser : public ISerializable
{
    uint uid;
    TVector2 position;

    void Serialize(BinaryEncoder& se) const override
    {
        se << uid << position.x << position.y;
    }
    void Deserialize(BinaryDecoder& se) override
    {
        se << uid << position.x << position.y;
    }
};
//--------------------------------------------------------- 
struct TankChess : public ISerializable
{
    uint rd_seed;
    uint rd_count;

    TankMap map;
    std::vector<TankUser> users;

    TankChess() : rd_seed(0), rd_count(0) {}

    void Serialize(BinaryEncoder& se) const override
    {
        se << rd_seed << rd_count;
        map.Serialize(se);

        size_t user_count = users.size();
        se << user_count;
        for (size_t i = 0; i < user_count; ++i)
        {
            users[i].Serialize(se);
        }
    }
    void Deserialize(BinaryDecoder& se) override
    {
        se << rd_seed << rd_count;
        map.Deserialize(se);

        size_t user_count = 0;
        se << user_count;

        for (size_t i = 0; i < user_count; ++i)
        {
            users.push_back(TankUser());
            users.back().Deserialize(se);
        }
    }
};
//--------------------------------------------------------- 
class TankUiGame : public TankGame
{
protected:
    TankGuiRender _render;
    TankEngineObserver _engine_observer;
public:
    void Init(TankChess& chess, uint zoom_size, TVector2 position, Ref<ITankFactory> factory)
    {
        // 边距和缩放
        _render.Position = ImVec2(position.x, position.y);
        _render.ZoomSize = zoom_size;

        _engine_observer.Clear();
        _engine_observer.AddRender(_render);
        TankGame::SetObserver(_engine_observer);

        // 绘制边框
        TankGame::Init(chess.map.w, chess.map.h, factory, chess.rd_seed, chess.rd_count);
        // 绘制地形
        for (size_t i = 0; i < chess.map.tiles.size(); ++i)
        {
            TankTile& t = chess.map.tiles[i];
            TankGame::Place(TankGame::Instantiate(t.class_type, t.group_type), t.x, t.y);
        }
        // 绘制角色
        for (size_t i = 0; i < chess.users.size(); ++i)
        {
            TankUser& u = chess.users[i];
            TankPlayer* p = ctype_cast(TankPlayer*)TankGame::Place(TankGame::Instantiate(class_Player), u.position.x, u.position.y);
            p->Uid = u.uid;
        }
    }
};
//--------------------------------------------------------- 
class TankUiWindow : public GuiWindow<GuiDX11>, public TankFactory, public RefObject
{
public:
    typedef GuiWindow<GuiDX11> GuiType;
public:
    static byte GetInputVector2(ImGuiKey w, ImGuiKey s, ImGuiKey a, ImGuiKey d)
    {
        if (ImGui::IsKeyDown(a))
        {
            return move_Left;
        }
        if (ImGui::IsKeyDown(d))
        {
            return move_Right;
        }
        if (ImGui::IsKeyDown(w))
        {
            return move_Up;
        }
        if (ImGui::IsKeyDown(s))
        {
            return move_Down;
        }
        return move_None;
    }
protected:
    TankUiGame _game;

    TankPlayer* _player;
    byte _key_direction;
    byte _key_shoot;

    bool _is_done;
public:
    TankUiWindow() : _player(NULL), _key_direction(0), _key_shoot(0), _is_done(false) {}

    void Exit()
    {
        _is_done = true;
    }

    virtual bool IsDone() const
    {
        return GuiType::IsDone() || _is_done;
    }

    virtual void InitGame(TankChess& chess)
    {
        // 初始化对局
        _game.Init(chess, 10, TVector2_ZERO, *this);

        _key_direction = 0;
        _key_shoot = 0;
        _is_done = false;
    }

    virtual void StartUp()
    {
        ::SetWindowPos(_env, NULL, 0, 0, 300, 300, SWP_NOZORDER | SWP_NOMOVE);
    }

    virtual void OnRender()
    {
        if (_player)
        {
            if (_key_direction == 0)
            {
                _key_direction = GetInputVector2(ImGuiKey_W, ImGuiKey_S, ImGuiKey_A, ImGuiKey_D);
            }
            if (_key_shoot == 0)
            {
                _key_shoot = ImGui::IsKeyPressed(ImGuiKey_J) ? 1 : 0;
            }
        }

        ImGui::SetNextWindowPos(ImVec2(0, 0));
        ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize);

        ImGui::Begin("Tank", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize);
        _game.Render();
        ImGui::End();
    }

    virtual bool FixedUpdate(Timer::TickType tick, uint delta)
    {
        if (ImGui::IsKeyDown(ImGuiKey_Q))
        {
            if (!IsDone())
            {
                Exit();
            }
            return false;
        }

        if (_player)
        {
            _game.AcceptInput(_player, _key_direction, _key_shoot);
        }

        _key_direction = 0;
        _key_shoot = 0;

        _game.Update();
        return true;
    }
};
//--------------------------------------------------------- 
} // namespace tank90
} // namespace game
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
 #endif // #ifndef _LIBZHOUYB_TANKUI_H_
//========================================================= 
